Castor XML is an XML data binding framework. Unlike the two main XML APIs, DOM (Document Object Model) and SAX (Simple API for XML) which deal with the structure of an XML document, Castor enables you to deal with the data defined in an XML document through an object model which represents that data.
Castor XML can marshal almost any "bean-like" Java Object to and from XML. In most cases the marshalling framework uses a set of ClassDescriptors and FieldDescriptors to describe how an Object should be marshalled and unmarshalled from XML.
For those not familiar with the terms "marshal" and "unmarshal", it's simply the act of converting a stream (sequence of bytes) of data to and from an Object. The act of "marshalling" consists of converting an Object to a stream, and "unmarshalling" from a stream to an Object.
The XML data binding framework, as it's name implies, is
responsible for doing the conversion between Java and XML.
The framework consists of two worker classes,
org/exolab/castor/xml/Marshaller
and
org.exolab.castor.xml.Unmarshaller
respectively, and a bootstrap class
org.exolab.castor.xml.XMLContext
used for configuration of the XML data binding framework and
instantiation of the two worker objects.
Lets walk through a very simple example. Assume we have a
simple
Person
class as follows:
import java.util.Date; /** An simple person class */ public class Person implements java.io.Serializable { /** The name of the person */ private String name = null; /** The Date of birth */ private Date dob = null; /** Creates a Person with no name */ public Person() { super(); } /** Creates a Person with the given name */ public Person(String name) { this.name = name; } /** * @return date of birth of the person */ public Date getDateOfBirth() { return dob; } /** * @return name of the person */ public String getName() { return name; } /** * Sets the date of birth of the person * @param name the name of the person */ public void setDateOfBirth(Date dob) { this.dob = dob; } /** * Sets the name of the person * @param name the name of the person */ public void setName(String name) { this.name = name; } }
To (un-)marshal data to and from XML, Castor XML can be used in one of three modes:
introspection mode
mapping mode
descriptor mode (aka generation mode)
The following sections discuss each of these modes at a high level.
The introspection mode is the simplest mode to use from a user perspective, as it does not require any configuration from the user. As such, the user does not have to provide any mapping file(s), nor point Castor to any generated descriptor classes (as discussed in the 'descriptor mode' section).
In this mode, the user makes use of
static
methods on the
org.exolab.castor.xml.Marshaller
and
org.exolab.castor.xml.Unmarshaller
classes, providing all required data as parameters on
these method calls.
To marshal an instance of the person class you simply
call the
org.exolab.castor.xml.Marshaller
as follows:
// Create a new Person Person person = new Person("Ryan 'Mad Dog' Madden"); person.setDateOfBirth(new Date(1955, 8, 15)); // Create a File to marshal to writer = new FileWriter("test.xml"); // Marshal the person object Marshaller.marshal(person, writer);
This produces the XML shown in Example 1.1, “XML produced in introspection mode”
To unmarshal an instance of the person class from XML,
you simply call the
org.exolab.castor.xml.Unmarshaller
as follows:
// Create a Reader to the file to unmarshal from reader = new FileReader("test.xml"); // Marshal the person object Person person = (Person) Unmarshaller.unmarshal(Person.class, reader);
Marshalling and unmarshalling is basically that simple.
Note | |
---|---|
Note: The above example uses the
static
methods of the marshalling framework, and as such no
Marshaller and/or Unmarshaller instances need to be
created. A common mistake in this context when using a
mapping file
is to call the
org.exolab.castor.xml.Marshaller or
org.exolab.castor.xml.Unmarshaller
as in the above example. This won't work, as the mapping
will be ignored.
|
In introspection mode , Castor XML uses Java reflection to establish the binding between the Java classes (and their properties) and the XML, following a set of (default) naming rules. Whilst it is possible to change to a different set of naming rules, there's no way to override this (default) naming for individual artifacts. In such a case, a mapping file should be used.
In mapping mode , the user provides Castor XML with a user-defined mapping (in form of a mapping file) that allows the (partial) definition of a customized mapping between Java classes (and their properties) and XML.
When you are using a mapping file, create an instance of the
org.exolab.castor.xml.XMLContext
class and use the
org.exolab.castor.xml.XMLContext.addMapping(Mapping)
method to provide Castor XML with one of more mapping files.
To start using Castor XML for marshalling and/or unmarshalling based upon your custom mapping, create
instances of org.exolab.castor.xml.Marshaller
and
org.exolab.castor.xml.Unmarshaller
as needed using one of the following methods:
Table 1.1. Methods on XMLContext to create Un-/Marshaller objects
Method name | Description |
---|---|
Creates a Marshaller instance. | |
Creates a Unmarshaller instance. |
and call any of the non-static (un)marshal methods to trigger data binding in either way.
Below code shows a full example that demonstrates
unmarshalling a
Person
instance from XML using a
org.exolab.castor.xml.Unmarshaller
instance as obtained from an XMLContext previously
configured to your needs.
Example 1.2. Unmarshalling from XML using a mapping
import org.exolab.castor.xml.XMLContext; import org.exolab.castor.mapping.Mapping; import org.exolab.castor.xml.Unmarshaller; // Load Mapping Mapping mapping = new Mapping(); mapping.loadMapping("mapping.xml"); // initialize and configure XMLContext XMLContext context = new XMLContext(); context.addMapping(mapping); // Create a Reader to the file to unmarshal from reader = new FileReader("test.xml"); // Create a new Unmarshaller Unmarshaller unmarshaller = context.createUnmarshaller(); unmarshaller.setClass(Person.class); // Unmarshal the person object Person person = (Person) unmarshaller.unmarshal(reader);
To marshal the very same
Person
instance to XML using a
org.exolab.castor.xml.Marshaller
obtained from the
same
org.exolab.castor.xml.XMLContext
,
use code as follows:
Example 1.3. Marshalling to XML using a mapping
import org.exolab.castor.xml.Marshaller; // create a Writer to the file to marshal to Writer writer = new FileWriter("out.xml"); // create a new Marshaller Marshaller marshaller = context.createMarshaller(); marshaller.setWriter(writer); // marshal the person object marshaller.marshal(person);
Please have a look at XML Mapping for a detailed discussion of the mapping file and its structure.
For more information on how to effectively deal with loading mapping file(s) especially in multi-threaded environments, please check the best practice section.
Castor supports multiple sources and destinations from which objects can be marshalled and unmarshalled.
Table 1.2. Marshalling destinations.
Destination | Description |
---|---|
|
The character stream. |
|
The SAX document handler. |
|
The SAX content handler. |
|
The DOM node to marshall object into. |
|
The STaX cursor API. |
|
The STaX iterator API. |
|
|
Table 1.3. Unmarshalling sources.
Source | Description |
---|---|
|
A character stream. |
|
A SAX input source. |
|
A W3C DOM node which will be used for unmarshalling. |
|
A StAX cursor. |
|
A StAX iterator. |
|
Supports |
Castor 1.3.2 and 1.3.3 introduced support for the STaX API for both for marshalling and unmarshalling. The framework fully supports the STaX cursor and iterator API.
An example of marshalling using STaX:
Example 1.4. Marshalling to a StAX java.xml.stream.XMLStreamWriter
// marshalling using STaX StringWriter writer = new StringWriter(); XMLOutputFactory outputFactory = XMLOutputFactory.newInstance(); XMLStreamWriter xmlStreamWriter = outputFactory.createXMLStreamWriter(writer); marshaller.setXmlStreamWriter(xmlStreamWriter); marshaller.marshal(object);
Also beginning from version 1.3.3, the framework has been modified to support Source and Result interfaces. Now it is possible to use SAXSource, DOMSource and StreamSource for unmarshalling and corresponding classes for marshalling.
Below an example of marshalling into Result:
Example 1.5. Marshalling to a javax.xml.transform.dom.DOMResult
// instance of object to be marshalled Object obj = ... // marshalling into DOM node XMLContext xmlContext = ... // creates the xml context // creates marshaller Marshaller marshaller = xmlContext.createMarshaller(); // creates DOM factory DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); // creates document Document document = builder.newDocument(); // sets the DOM result for the marshaller marshaller.setResult(new DOMResult(document)); // marshalls object marshaller.marshall(obj);
Another example of unmarshalling from Source:
Example 1.6. Unmarshalling from a javax.xml.transform.sax.SAXSource
// unmarshalling from SAX InputSource XMLContext xmlContext = ... // creates the xml context // creates unmarshaller Unmarshaller unmarshaller = xmlContext.createUnmarshaller(); // creates SAX input source InputSource inputSource = new InputSource(new StringReader(xml)); // creates instance of SAXSource SAXSource saxSource = new SAXSource(inputSource); // unmarshalls object Object result = unmarshaller.unmarshal(saxSource);
With Castor 1.1.2, the
org.exolab.castor.xml.XMLContext
class has been added to the Castor marshalling framework.
This new class provides a bootstrap mechanism for Castor
XML, and allows easy (and efficient) instantiation of
org.exolab.castor.xml.Marshaller
and
org.exolab.castor.xml.Unmarshaller
instances as needed.
As shown above, the
org.exolab.castor.xml.XMLContext
class offers various factory methods to obtain a new
org.exolab.castor.xml.Marshaller
,
org.exolab.castor.xml.Unmarshaller
.
When you need more than one
org.exolab.castor.xml.Unmarshaller
instance in your application, please call
org.exolab.castor.xml.XMLContext.createUnmarshaller()
as required. As all
Unmarshaller
instances are created from the very same
XMLContext
instance, overhead will be minimal. Please note, though,
that use of one
Unmarshaller
instance is not thread-safe.
Castor can marshal "almost" any arbitrary Object to and from XML. When descriptors are not available for a specfic Class, the marshalling framework uses reflection to gain information about the object.
Note | |
---|---|
Actually an in memory set of descriptors are created for the object and we will soon have a way for saving these descriptors as Java source, so that they may be modified and compiled with little effort. |
If a set of descriptors exist for the classes, then Castor will use those to gain information about how to handle the marshalling. See Section 1.1.6, “Class Descriptors” for more information.
There is one main restrictions to marshalling objects. These classes must have have a public default constructor (ie. a constructor with no arguments) and adequete "getter" and "setter" methods to be properly be marshalled and unmarshalled.
The example illustrated in the previous section Section 1.1.2, “Castor XML - The XML data binding framework” demonstrates how to use the framework with existing classes.
Class descriptors provide the "Castor Framework" with necessary information so that the Class can be marshalled properly. The class descriptors can be shared between the JDO and XML frameworks.
Class descriptors contain a set of ???
XML Class descriptors provide the marshalling framework with
the information it needs about a class in order to be
marshalled to and from XML. The XMLClassDescriptor
org.exolab.castor.xml.XMLClassDescriptor
.
XML Class Descriptors are created in four main ways. Two of these are basically run-time, and the other two are compile time.
To use "compile-time" class descriptors, one can
either implement the
org.exolab.castor.xml.XMLClassDescriptor
interface for each class which needs to be
"described", or have the
Source Code Generator
create the proper descriptors.
The main advantage of compile-time descriptors is that they are faster than the run-time approach.
To use "run-time" class descriptors, one can either simply let Castor introspect the classes, a mapping file can be provided, or a combination of both "default introspection" and a specified mapping file may be used.
For "default introspection" to work the class being introspected must have adequete setter/getter methods for each field of the class that should be marshalled and unmarshalled. If no getter/setter methods exist, Castor can handle direct field access to public fields. It does not do both at the same time. So if the respective class has any getter/setter methods at all, then no direct field access will take place.
There is nothing to do to enable "default introspection". If a descriptor cannot be found for a class, introspection occurs automatically.
Some behavior of the introspector may be controlled by setting the appropriate properties in the castor.properties file. Such behavior consists of changing the naming conventions, and whether primitive types are treated as attributes or elements. See castor.properties file for more information.
A mapping file may also be used to "describe" the
classes which are to be marshalled. The mapping is
loaded before any marshalling/unmarshalling takes
place. See
org.exolab.castor.mapping.Mapping
The main advantage of run-time descriptors is that it takes very little effort to get something working.
Castor XML mapping is a way to simplify the binding of java classes to XML document. It allows to transform the data contained in a java object model into/from an XML document.
Although it is possible to rely on Castor's default behavior to marshal and unmarshal Java objects into an XML document, it might be necessary to have more control over this behavior. For example, if a Java object model already exists, Castor XML Mapping can be used as a bridge between the XML document and that Java object model.
Castor allows one to specify some of its marshalling/unmarshalling behavior using a mapping file. This file gives explicit information to Castor on how a given XML document and a given set of Java objects relate to each other.
A Castor mapping file is a good way to dissociate the changes in the structure of a Java object model from the changes in the corresponding XML document format.
The mapping information is specified by an XML document. This document is written from the point of view of the Java object and describes how the properties of the object have to be translated into XML. One constraint for the mapping file is that Castor should be able to infer unambiguously from it how a given XML element/attribute has to be translated into the object model during unmarshalling.
The mapping file describes for each object how each of its fields have to be mapped into XML. A field is an abstraction for a property of an object. It can correspond directly to a public class variable or indirectly to a property via some accessor methods (setters and getters).
It is possible to use the mapping and Castor default behavior in conjunction: when Castor has to handle an object or an XML data but can't find information about it in the mapping file, it will rely on its default behavior. Castor will use the Java Reflection API to introspect the Java objects to determine what to do.
Note: Castor can't handle all possible mappings. In some complex cases, it may be necessary to rely on an XSL transformation in conjunction with Castor to adapt the XML document to a more friendly format.
For Castor, a Java class has to map into an XML element. When Castor marshals an object, it will:
use the mapping information, if any, to find the name of the element to create
or
by default, create a name using the name of the class
It will then use the fields information from the mapping file to determine how a given property of the object has to be translated into one and only one of the following:
an attribute
an element
text content
nothing, as we can choose to ignore a particular field
This process will be recursive: if Castor finds a property that has a class type specified elsewhere in the mapping file, it will use this information to marshal the object.
By default, if Castor finds no information for a given class in the mapping file, it will introspect the class and apply a set of default rules to guess the fields and marshal them. The default rules are as follows:
All primitive types, including the primitive type wrappers (Boolean, Short, etc...) are marshalled as attributes.
All other objects are marshalled as elements with either text content or element content.
When Castor finds an element while unmarshalling a document, it will try to use the mapping information to determine which object to instantiate. If no mapping information is present, Castor will use the name of the element to try to guess the name of a class to instantiate (for example, for an element named 'test-element', Castor will try to instantiate a class named 'TestElement' if no information is given in the mapping file). Castor will then use the field information of the mapping file to handle the content of the element.
If the class is not described in the mapping file, Castor will instrospect the class using the Java Reflection API to determine if there is any function of the form getXxxYyy()/setXxxYyy(<type> x). This accessor will be associated with XML element/attribute named 'xxx-yyy'. In the future, we will provide a way to override this default behavior.
Castor will introspect object variables and use direct access _only_ if no get/set methods have been found in the class. In this case, Castor will look for public variables of the form:
public <type> xxxYYY;
and expect an element/attribute named 'xxx-yyy'. The only handled collections for <type> are java.lang.Vector and array. (up to version 0.8.10)
For primitive <type>, Castor will look for an attribute first and then an element. If <type> is not a primitive type, Castor will look for an element first and then an attribute.
The following sections define the syntax for each of the mapping file artefacts and their semantical meaning.
This section defines a small domain model that will be referenced by
various mapping file (fragments/samples) in the following sections. The model consists
of two two classes Order
and OrderItem
, where an order
holds a list of order items.
public class Order { private List orderItems; private String orderNumber; public List getOrderItems() { return orderItems; } public void setOrderItems(List orderItems) { this.orderItems = orderItems; } public String getOrderNumber() { return orderNumber; } public void setOrderNumber(String orderNumber) { this.orderNumber = orderNumber; } } public class OrderItem { private String id; private Integer orderQuantity; public String getId() { return id; } public void setId(String id) { this.id = id; } public Integer getOrderQuantity() { return orderQuantity; } public void setOrderQuantity(Integer orderQuantity) { this.orderQuantity = orderQuantity; } }
As shown above in bold, the Order
instance has a (private) field 'orderItems'
to hold a collection of OrderItem
instances. This field is publically exposed
by corresponding getter and setter methods.
<!ELEMENT mapping ( description?, include*, field-handler*, class*, key-generator* )>
The <mapping> element is the root element of a mapping file. It contains:
an optional description
zero or more <include> which facilitates reusing mapping files
zero of more <field-handler> defining custom, configurable field handlers
zero or more <class> descriptions: one for each class we intend to give mapping information
zero or more <key-generator>: not used for XML mapping
A mapping file look like this:
<?xml version="1.0"?> <!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN" castor.org "http://castor.org/mapping.dtd"> <mapping> <description>Description of the mapping</description> <include href="other_mapping_file.xml"/> <!-- mapping for class 'A' --> <class name="A"> ......... </class> <!-- mapping for class 'B' --> <class name="B"> ......... </class> </mapping>
<!ELEMENT class ( description?, cache-type?, map-to?, field+ )> <!ATTLIST class name ID #REQUIRED extends IDREF #IMPLIED depends IDREF #IMPLIED auto-complete ( true |false ) "false" identity CDATA #IMPLIED access ( read-only | shared | exclusive | db-locked ) "shared" key-generator IDREF #IMPLIED >
The <class>
element contains all the information used to
map a Java class into an XML document. The content of <class>
is mainly used to describe the fields that will be mapped.
Table 1.4. Description of the attributes
Name | Description |
---|---|
name | The fuly-qualified name of the Java class that we want to map to. |
extends | The fully qualified name of a parent class. This attribute should be used only if this class extends another class for which a class mapping is provided. It should not be used if there's no class maping for the extended class. |
depends | Used with Castor JDO only; for more information on this field, please see the JDO documentation. |
auto-complete | If true, the class will be introspected to determine its field and the fields specified in the mapping file will be used to overide the fields found during the introspection. |
identity | Used with Castor JDO only; for more information on this field, please see see the JDO documentation. |
access | Used with Castor JDO only; for more information on this field, please see see the JDO documentation. |
key-generator | Used with Castor JDO only; for more information on this field, please see see the JDO documentation. |
The auto-complete attributes is interesting as it allow a fine degree of control of the introspector: it is possible to specifiy only the fields whose Castor default behavior does not suite our needs. These feature should simplify the handling of complexe class containing many fields. Please see below for an example usage of this attribute.
Table 1.5. Description of the content
Name | Description |
---|---|
description | An optional description. |
cache-type | Used with Castor JDO only; for more information on this field, please see see the JDO documentation. |
map-to | Used if the name of the element is not the name of the class. By default, Castor will infer the name of the element to be mapped from the name of the class: a Java class named 'XxxYyy' will be transformed in 'xxx-yyy'. If you don't want Castor to generate the name, you need to use <map-to> to specify the name you want to use. <map-to> is only used for the root element. |
field | Zero or more <field> elements, which are used to describe the properties of the Java class being mapped. |
The following mapping fragment defines a class mapping for the OrderItem
class:
<class name="mypackage.OrderItem> <map-to xml="item"/> <field name="id" type="string"> <bind-xml name="identity" node="attribute"/> </field> </field name="orderQuantity" type="integer"> <bind-xml name="quantity" node="element"/> </field> </class>
When marshalling an OrderItem
instance, this yields the
following XML:
<?xml version="1.0" ?> <item identity="12"> <quantity>100</quantity> </item>
The following mapping fragment defines a class mapping for the same
class, where for all properties but id
introspection should
be used; the use of the auto-complete
attribute instructs Castor XML
to use introspection for all attributes other than 'id'
, where
the given field mapping will be used.
<class name="mypackage.OrderItem auto-complete="true"> <map-to xml="item"/> <field name="id" type="string"> <bind-xml name="identity" node="attribute"/> </field> </class>
When marshalling the very same OrderItem
instance, this yields the
following XML:
<?xml version="1.0" ?> <item identity="12"> <order-quantity>100</order-quantity> </item>
By removing the <map-to> element from above class mapping, ...
<class name="mypackage.OrderItem auto-complete="true"> <field name="id" type="string"> <bind-xml name="identity" node="attribute"/> </field> </class>
... Castor will use introspection to infer the element name from the Java
class name (OrderItem
), applying a default naming convention scheme.
When marshalling the very same OrderItem
instance, this yields the
following XML:
<?xml version="1.0" ?> <order-item identity="12"> <order-quantity>100</order-quantity> </order-item>
<!ELEMENT map-to EMPTY>
<!ATTLIST map-to
table NMTOKEN #IMPLIED
xml NMTOKEN #IMPLIED
ns-uri NMTOKEN #IMPLIED
ns-prefix NMTOKEN #IMPLIED
ldap-dn NMTOKEN #IMPLIED
element-definition (true|false) "false" NEW as of 1.0M3
ldap-oc NMTOKEN #IMPLIED>
<map-to>
is used to specify the name of the element that should
be associated with the given class. <map-to>
is only used for
the root class. If this information is not present, Castor will:
for marshalling, infer the name of the element to be mapped from the name of the class: a Java class named 'XxxYyy' will be transformed into 'xxx-yyy'.
for unmarshalling, infer the name of the class from the name of the element: for an element named 'test-element' Castor will try to use a class named 'TestElement'
Please note that it is possible to change the naming scheme used by Castor
to translate between the XML name and the Java class name in the
castor.properties
file.
Table 1.6. Description of attributes
xml | Name of the element that the class is associated to. |
ns-uri | Namespace URI |
ns-prefix | Desired namespace |
element-definition | True if the descriptor as created from a schema definition that
was of type element (as opposed to a <complexType> definition). This
only is useful in the context of source code generation. |
ldap-dn | Not used for Castor XML |
ldap-oc | Not used for Castor XML |
The following mapping fragment defines a <map-to element for the
OrderItem
class, manually setting the element name to a value
of 'item'
.
<class name="myPackage.OrderItem"> ... <map-to xml="item" /> ... </class>
The following mapping fragment instructs Castor to assign a namespace URI of
http://castor.org/sample/mapping/
to the <item> element,
and use a namespace prefix of 'castor'
during un-/marshalling.
<class name="myPackage.OrderItem"> ... <map-to xml="item" ns-uri="http://castor.org/sample/mapping/" ns-prefix="castor"/> ... </class>
When marshalling an OrderItem
instance, this will yield the
following XML:
<?xml version="1.0" ?> <castor:order-item xmlns:castor="http://castor.org/sample/mapping/" identity="12"> <castor:order-quantity>100</castor:order-quantity> </castor:order-item>
<!ELEMENT field ( description?, sql?, bind-xml?, ldap? )> <!ATTLIST field name NMTOKEN #REQUIRED type NMTOKEN #IMPLIED handler NMTOKEN #IMPLIED required ( true | false ) "false" direct ( true | false ) "false" lazy ( true | false ) "false" transient ( true | false ) "false" nillable ( true | false ) "false" container ( true | false ) "false" get-method NMTOKEN #IMPLIED set-method NMTOKEN #IMPLIED create-method NMTOKEN #IMPLIED collection ( array | vector | hashtable | collection | set | map ) #IMPLIED>
<field>
is used to describe a property of a Java object
we want to marshal/unmarshal. It gives:
its identity ('name')
its type (infered from 'type' and 'collection')
its access method (infered from 'direct', 'get-method', 'set-method')
From this information, Castor is able to access a given property in the Java class.
In order to determine the signature that Castor expects, there are two easy rules to apply.
1. Determine <type>.
If there is no 'collection' attribute, the <type> is just the Java type specified in <type_attribute> (the value of the 'type' attribute in the XML document). The value of <type_attribute> can be a fully qualified Java object like 'java.lang.String' or one of the allowed short name:
Table 1.7. Type shortnames
short name | Primitive type? | Java Class |
---|---|---|
other | N | java.lang.Object |
string | N | java.lang.String |
integer | Y | java.lang.Integer.TYPE |
long | Y | java.lang.Long.TYPE |
boolean | Y | java.lang.Boolean.TYPE |
double | Y | java.lang.Double.TYPE |
float | Y | java.lang.Float.TYPE |
big-decimal | N | java.math.BigDecimal |
byte | Y | java.lang.Byte.TYPE |
date | N | java.util.Date |
short | Y | java.lang.Short.TYPE |
char | Y | java.lang.Character.TYPE |
bytes | N | byte[] |
chars | N | char[] |
strings | N | String[] |
locale | N | java.util.Locale |
Castor will try to cast the data in the XML file in the proper Java type.
If there is a collection attribute , you can use the following table:
Table 1.8. Type implementations
name | <type> | default implementation |
---|---|---|
array | <type_attribute>[] | <type_attribute>[] |
arraylist | java.util.List | java.util.Arraylist |
vector | java.util.Vector | java.util.Vector |
hashtable | java.util.Hashtable | java.util.Hashtable |
collection | java.util.Collection | java.util.Arraylist |
set | java.util.Set | java.util.Hashset |
map | java.util.Map | java.util.Hashmap |
sortedset | java.util.SortedSet | java.util.TreeSet |
The type of the object inside the collection is <type_attribute>. The 'default implementation' is the type used if the object holding the collection is found to be null and need to be instantiated.
For hashtable and maps (since 0.9.5.3), Castor will save both key and values. When marshalling output <key> and <value> elements. These names can be controlled by using a top-level or nested class mapping for the org.exolab.castor.mapping.MapItem class.
Note: for backward compatibility with prior versions of Castor, the saveMapKeys property can be set to false in the castor.properties file.
For versions prior to 0.9.5.3, hashtable and maps, Castor will save only the value during marshalling and during unmarshalling will add a map entry using the object as both the key and value, e.g. map.put(object, object).
It is necessary to use a collection when the content model of the element expects more than one element of the specified type.
Determine the signature of the function
If 'direct' is set to true, Castor expects to find a class variable with the given signature:
public <type> <name>;
If 'direct' is set to false or omitted, Castor will access the property though accessor methods. Castor determines the signature of the accessors as follow: If the 'get-method' or 'set-method' attributes are supplied, it will try to find a function with the following signature:
public <type> <get-method>();
or
public void <set-method>(<type> value);
If 'get-method' and 'set-method' attributes are not provided, Castor will try to find the following function:
public <type> get<capitalized-name>();
or
public void set<capitalized-name>(<type> value);
<capitalized-name> means that Castor takes the <name> attribute and put its first letter in uppercase without modifying the other letters.
The content of <field> will contain the information on how to map this given field to SQL, XML, ...
Exceptions concerning collection fields:
The default is to treat the 'get-method' as a simple getter returning the collection field, and the 'set-method' as a simple getter used to set a new instance on the collection field.
Table 1.9. Collection field access
Parameter | Description |
---|---|
'get-method' |
If a 'get-method' is provided for a collection field, Castor - in adition to the default behaviour described above - will deviate from the standard case for the following special prefixes:
public Iterator iterate...();
A 'get-method' starting with the
prefix '
public Enumeration enum...();
A 'get-method' starting with '
|
'set-method' |
If 'set-method' is provided for a collection field, Castor - in addition to the default behaviour described above - will accept an 'add' prefix and expect the following signature: public void add...(<type> value); This method is called for each collection element while unmarshalling. |
Table 1.10. Description of the attributes
Name | Description |
---|---|
name | The field 'name' is required even if no such field exists in the class. If 'direct' access is used, 'name' should be the name of a public instance member in the object to be mapped (the field must be public, not static and not transient). If no direct access and no 'get-/set-method' is specified, this name will be used to infer the name of the accessors methods. |
type | The Java type of the field. It is used to access the field. Castor will use this information to cast the XML information (like string into integer). It is also used to define the signature of the accessor methods. If a collection is specified, this is used to specify the type of the objects held by the collection. See description above for more details. |
required | A field can be optional or required. |
nillable | A field can be of content 'nil '. |
transient | If true, this field will be ignored during the marshalling. This is usefull when used together with the auto-complete="true" option. |
direct | If true, Castor will expect a public variable in the containing class and will access it directly (for both reading and writing). |
container | Indicates whether the field should be treated as a container, i.e. only it's fields should be persisted, but not the containing class itself. In this case, the container attribute should be set to true (supported in Castor XML only). |
collection | If a parent expects more than one occurrence of one of its element, it is necessary to specify which collection Castor will use to handle them. The type specified is used to define the type of the content inside the collection. |
get-method | Optional name of the 'get method' Castor should use. If this attribute is not set and the set-method attribute is not set, then Castor will try to infer the name of this method with the algorithm described above. |
set-method | Optional name of the 'set method' Castor should use. If this attribute is not set and the get-method attribute is not set, then Castor will try to infer the name of this method with the algorithm described above. |
create-method | Optionally defines a factory method for the instantiation of a FieldHandler |
handler | If present, specifies one of the following:
|
In the case of XML mapping, the content of a field element should be one and only one <bind-xml> element describing how this given field will be mapped into the XML document.
Starting with release 0.9.5, for attribute
mapped fields, support has been added to map a constructor field using
the set-method
attribute.
To specify that a field (mapped to an attribute) should be used as a constructor
argument during object initialization, please specify a set-method
attribute on the
<field>
mapping and use "%X" as the value of the
set-method
attribute, where X
is a positive integer number, e.g. %1
or
%21
.
For example:
<field name="foo" set-method="%1" get-method="getFoo" type="string"> <bind-xml node="attribute"/> </field>
Note that because the set-method
is specified, the get-method
also
must be specified.
Tip: the XML HOW-TO section has a HOW-TO document for mapping constructor arguments, incl. a fully working mapping.
The following mapping fragment defines a <field>
element for the
member
property of the org.some.package.Root
class,
specifying a custom org.exolab.castor.mapping.FieldHandler
implementation.
<class name="org.some.package.Root"> <field name="member" type="string" handler="org.some.package.CustomFieldHandlerImpl"/> </class>
The same custom field handler as in the previous sample can be defined with a separate configurable <field-handler> definition, where additional configuration can be provided.
<field-handler name="myHandler" class="org.some.package.CustomFieldHandlerImpl"> <param name="date-format" value="yyyyMMddHHmmss"/> </field-handler>
and subsequently be referred to by its name as shown in the following field mapping:
<class name="org.some.package.Root"> <field name="member" type="string" handler="myHandler"/> </class>
Assume you have a class mapping for a class Order
which defines -
amongst others - a field mapping as follows, where the field item
refers to an instance of a class Item
.
<class name="some.example.Order"> ... <field name="item" type="some.example.Item" > <bind-xml> name="item" node="element" /> </field> ... </class> <class name="some.example.Item"> <field name="id" type="long" /> <field name="description" type="string" /> </class>
Marshalling an instance of Order
would produce XML as follows:
<order> ... <item> <id>100</id> <description>...</description> </item> </order>
If you do not want the Item
instance to be marshalled,
but only its fields, change the field mapping for the item
member to be as follows:
<field name="item" type="some.example.Item" container="false" > <bind-xml> name="item" node="element" /> </field>
The resulting XML would look as follows:
<order> ... <id>100</id> <description>...</description> </order>
<!ELEMENT bind-xml (class?, property*)> <!ATTLIST bind-xml name NMTOKEN #IMPLIED type NMTOKEN #IMPLIED location CDATA #IMPLIED matches NMTOKENS #IMPLIED QName-prefix NMTOKEN #IMPLIED reference ( true | false ) "false" node ( attribute | element | text ) #IMPLIED auto-naming ( deriveByClass | deriveByField ) #IMPLIED transient ( true | false ) "false">
Table 1.11. Description of the attributes
name | The name of the element or attribute.
| |||
auto-naming | If no name is specified, this attribute controls how castor will automatically create a name for the field. Normally, the name is created using the field name, however many times it is necessary to create the name by using the class type instead (such as heterogenenous collections). | |||
type | XML Schema type (of the value of this field) that requires specific handling in the Castor Marshalling Framework (such as 'QName' for instance). | |||
location (since 0.9.4.4) | Allows the user to specify the "sub-path" for which the value should be marshalled to and from. This is useful for "wrapping" values in elements or for mapping values that appear on sub-elements to the current "element" represented by the class mapping. For more information, see the Location attribute below. | |||
QName-prefix | When the field represents a QName value, a prefix can be provided that is used when marshalling value of type QName. More information on the use of 'QName-prefix' can be found in the SourceGenerator Documentation | |||
reference | Indicates if this field has to be treated as a reference by the
unmarshaller. In order to work properly, you must specify the node
type to 'attribute' for both the 'id' and the 'reference'. In
newer versions of Castor, 'element' node for reference is allowed.
Remember to make sure that an identity field is specified
on the <class> mapping for the object type being
referenced so that Castor knows what the object's identity is. | |||
matches | Allows overriding the matches rules for the name of the element. It is a standard regular expression and will be used instead of the 'name' field. A '*' will match any xml name, however it will only be matched if no other field exists that matches the xml name. | |||
node | Indicates if the name corresponds to an attribute, an element, or text content. By default, primitive types are assumed to be an attribute, otherwise the node is assumed to be an elemen | |||
transient | Allows for making this field transient for XML. The default value is inherited from the <field> element. |
Since 0.9.5.3, the bind-xml element supports a nested class mapping, which is often useful when needing to specify more than one mapping for a particular class. A good example of this is when mapping Hashtable/HashMap/Map.
<bind-xml ...> <class name="org.exolab.castor.mapping.MapItem"> <field name="key" type="java.lang.String"> <bind-xml name="id"/> </field> <field name="value" type="com.acme.Foo"/> </class> </bind-xml>
Here is an example of how Castor Mapping can be used. We want to map an XML document like the following one (called 'order.xml'). model.
<Order reference="12343-AHSHE-314159"> <Client> <Name>Jean Smith</Name> <Address>2000, Alameda de las Pulgas, San Mateo, CA 94403</Address> </Client> <Item reference="RF-0001"> <Description>Stuffed Penguin</Description> <Quantity>10</Quantity> <UnitPrice>8.95</UnitPrice> </Item> <Item reference="RF-0034"> <Description>Chocolate</Description> <Quantity5</Quantity> <UnitPrice>28.50</UnitPrice> </Item> <Item reference="RF-3341"> <Description>Cookie</Description> <Quantity>30</Quantity> <UnitPrice>0.85</UnitPrice> </Item> </Order>
Into the following object model composed of 3 classes:
MyOrder: represent an order
Client: used to store information on the client
Item: used to store item in an order
The sources of these classes follow.
import java.util.Vector; import java.util.Enumeration; public class MyOrder { private String _ref; private ClientData _client; private Vector _items; private float _total; public void setReference(String ref) { _ref = ref; } public String getReference() { return _ref; } public void setClientData(ClientData client) { _client = client; } public ClientData getClientData() { return _client; } public void setItemsList(Vector items) { _items = items; } public Vector getItemsList() { return _items; } public void setTotal(float total) { _total = total; } public float getTotal() { return _total; } // Do some processing on the data public float getTotalPrice() { float total = 0.0f; for (Enumeration e = _items.elements() ; e.hasMoreElements() ;) { Item item = (Item) e.nextElement(); total += item._quantity * item._unitPrice; } return total; } }
public class ClientData { private String _name; private String _address; public void setName(String name) { _name = name; } public String getName() { return _name; } public void setAddress(String address) { _address = address; } public String getAddress() { return _address; } }
public class Item { public String _reference; public int _quantity; public float _unitPrice; public String _description; }
The XML document and the java object model can be connected by using the following mapping file:
<?xml version="1.0"?> <!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN" "http://castor.org/mapping.dtd"> <mapping> <class name="MyOrder"> <map-to xml="Order"/> <field name="Reference" type="java.lang.String"> <bind-xml name="reference" node="attribute"/> </field> <field name="Total" type="float"> <bind-xml name="total-price" node="attribute"/> </field> <field name="ClientData" type="ClientData"> <bind-xml name="Client"/> </field> <field name="ItemsList" type="Item" collection="vector"> <bind-xml name="Item"/> </field> </class> <class name="ClientData"> <field name="Name" type="java.lang.String"> <bind-xml name="Name" node="element"/> </field> <field name="Address" type="java.lang.String"> <bind-xml name="Address" node="element"/> </field> </class> <class name="Item"> <field name="_reference" type="java.lang.String" direct="true"> <bind-xml name="reference" node="attribute"/> </field> <field name="_quantity" type="integer" direct="true"> <bind-xml name="Quantity" node="element"/> </field> <field name="_unitPrice" type="float" direct="true"> <bind-xml name="UnitPrice" node="element"/> </field> <field name="_description" type="string" direct="true"> <bind-xml name="Description" node="element"/> </field> </class> </mapping>
The following class is an example of how to use Castor XML Mapping to manipulate the file 'order.xml'. It unmarshals the document 'order.xml', computes the total price, sets the total price in the java object and marshals the object model back into XML with the calculated price.
import org.exolab.castor.mapping.Mapping; import org.exolab.castor.mapping.MappingException; import org.exolab.castor.xml.Unmarshaller; import org.exolab.castor.xml.Marshaller; import java.io.IOException; import java.io.FileReader; import java.io.OutputStreamWriter; import org.xml.sax.InputSource; public class main { public static void main(String args[]) { Mapping mapping = new Mapping(); try { // 1. Load the mapping information from the file mapping.loadMapping( "mapping.xml" ); // 2. Unmarshal the data Unmarshaller unmar = new Unmarshaller(mapping); MyOrder order = (MyOrder)unmar.unmarshal(new InputSource(new FileReader("order.xml"))); // 3. Do some processing on the data float total = order.getTotalPrice(); System.out.println("Order total price = " + total); order.setTotal(total); // 4. marshal the data with the total price back and print the XML in the console Marshaller marshaller = new Marshaller(new OutputStreamWriter(System.out)); marshaller.setMapping(mapping); marshaller.marshal(order); } catch (Exception e) { System.out.println(e); return; } } }
Ordinarily, a mapping will only reference types that are concrete classes (i.e. not interfaces nor abstract classes). The reason is that to unmarshal a type requires instantiating it and one cannot instantiate an interface. However, in many real situations, object models depend on the use of interfaces. Many class properties are defined to have interface types to support the ability to swap implementations. This is often the case in frameworks.
The problem is that a different mapping must be used each time the same model is to be used to marshal/unmarshal an implementation that uses different concrete types. This is not convenient. The mapping should represent the model and the specific concrete type used to unmarshal a document is a configuration parameter; it should be specified in the instance document to be unmarshalled, not the mapping.
For example, assume a very simple object model of an engine that has one property that is a processor:
public interface IProcessor { public void process(); } public class Engine { private IProcessor processor; public IProcessor getProcessor() { return processor; } public void setProcessor(IProcessor processor) { this.processor = processor; } }
A typical mapping file for such a design may be:
<mapping> <class name="Engine"> <map-to xml="engine" /> <field name="processor" type="IProcessor" required="true"> <bind-xml name="processor" node="element" /> </field> </class> </mapping>
It is possible to use such a mapping and still have the marshal/unmarshal process work by specifying the concrete implementation of IProcessor in the document to be unmarshalled, using the xsi:type attribute, as follows:
<engine>
<processor xsi:type="java:com.abc.MyProcessor" />
</engine>
In this manner, one is still able to maintain only a single mapping, but vary the manner in which the document is unmarshalled from one instance document to the next. This flexibility is powerful because it enables the support of polymorphism within the castor xml marshalling framework.
Suppose we wanted the following XML instead:
<engine> <myProcessor/> </engine>
In the above output our XML name changed to match the type of the class used instead of relying on the xsi:type attribute. This can be achieved by modifying the mapping file as such:
<mapping> <class name="Engine"> <map-to xml="engine" /> <field name="processor" type="IProcessor" required="true"> <bind-xml auto-naming="deriveByClass" node="element" /> </field> </class> <class name="MyProcessor"> <map-to xml="myProcessor" /> </class> </mapping>
Since 0.9.5
The location attribute allows the user to map fields from nested elements or specify a wrapper element for a given field. Wrapper elements are simply elements which appear in the XML instance, but do not have a direct mapping to an object or field within the object model.
For example to map an instance of the following class:
public class Foo { private Bar bar = null; public Foo(); public getBar() { return bar; } public void setBar(Bar bar) { this.bar = bar; } }
into the following XML instance:
<?xml version="1.0"?> <foo>; <abc> <bar>...</bar> </abc> </foo>
(notice that an 'abc' field doesn't exist in the Bar class) One would use the following mapping:
<?xml version="1.0"?> ... <class name="Foo"> <field name="bar" type="Bar"> <bind-xml name="bar" location="abc"/> </field> </class> ... </mapping>
Note the "location" attribute. The value of this attribute is the name of the wrapper element. To use more than one wrapper element, the name is separated by a forward-slash as such:
<bind-xml name="bar" location="abc/xyz" />
Note that the name of the element is not part of the location itself and that the location is always relative to the class in which the field is being defined. This works for attributes also:
<bind-xml name="bar" location="abc" node="attribute" />
will produce the following:
<?xml version="1.0"?> <foo> <abc bar="..."/>; </foo>
Some helpful hints...
Castor comes with a tool that can automatically create a mapping from class files. Please see the XML FAQ for more information.
Sometimes to handle complex situations you'll need to create your own FieldHandler. Normally a FieldHandler deals with a specific class and field, however generic, reusable FieldHandlers can also be created by extending org.exolab.castor.mapping.GeneralizedFieldHandler or org.exolab.castor.mapping.AbstractFieldHandler. The FieldHandler can be specified on the <field> element.
For more information on writing a custom FieldHandler please see the following: XML FieldHandlers.
You may map any attributes to constructor arguments. For more information on how to map constructor arguments see the information available in the section on set-method above.
Please note that mapping elements to constructor arguments is not yet supported.
Tip: the XML HOW-TO section has a HOW-TO document for mapping constructor arguments.
Sometimes it's useful to prevent Castor from checking for a default constructor, such as when trying to write a mapping for an interface or type-safe enum. You can use the "undocumented" verify-constructable="false" attribute on the <class> element to prevent Castor from looking for the default constructor.
While you can always use your own custom FieldHandler for handling type-safe enumeration classes, Castor does have a built-in approach to dealing with these types of classes. If the type-safe enum class has a public static <type> valueOf(String) method Castor will call that method so that the proper instance of the enumeration is returned. Note: You'll also need to disable the default constructor check in the mapping file (see section 7.4 above to see more on this).
Before using the
Marshaller
class for marshalling Java objects to XML, the
Marshaller
can be fine-tuned according to your needs by calling a
variety of set-methods on this class. This section enlists
the available properties and provides you with information
about their meaning, possible values and the default value.
Table 1.12. Marshaller properties
Name | Description | Values | Default | Since |
---|---|---|---|---|
suppressNamespaces |
true
or
false
|
false
| - |
Before using the
Unmarshaller
class for unmarshalling Java objects from XML, the
Unmarshaller
can be fine-tuned according to your needs by calling a
variety of set-methods on this class. This section enlists
the available properties and provides you with information
about their meaning, possible values and the default value.
Table 1.13. Unmarshaller properties
Name | Description | Values | Default | Since |
---|---|---|---|---|
rootObject | A Class instance identifying the root class to use for unmarshalling. | - | - |
Being an XML data binding framework by definition, Castor XML relies on the availability of an XML parser at run-time. In Java, an XML parser is by default accessed though either the DOM or the SAX APIs: that implies that the XML Parser used needs to comply with either (or both) of these APIs.
With the creation of the JAXP API (and its addition to the Java language definition as of Java 5.0), Castor internally has been enabled to allow usage of the JAXP interfaces to interface to XML parsers. As such, Castor XML allows the use of a JAXP-compliant XML parser as well.
By default, Castor ships with Apache Xerces 2.6.2. You may, of course, upgrade to a newer version of Apache Xerces at your convenience, or switch to any other XML parser as long as it is JAXP compliant or implements a particular SAX interface. Please note that users of Java 5.0 and above do not need to have Xerces available at run-time, as JAXP and Xerces have both been integrated into the run-time library of Java.
For marshalling, Castor XML can equally use any JAXP complaint XML parser (or
interact with an XML parser that implements the SAX API), with the exception
of the following special case: when using 'pretty printing' during marshalling
(by setting the corresponding property in castor.properties
to true
) with Java 1.4 or below,
Apache Xerces
has to be on the classpath, as Castor XML internally uses Xerces' XMLSerializer
to implement this feature.
The following table enlists the requirements relative to the Java version used in your environment.
Table 1.14. XML APIs on various Java versions
Java 1.4 and below | Java 5.0 and above |
---|---|
Xerces 2.6.2 | - |
XML APIs | - |
As of Castor 1.3.2, Castor XML can be used with a StAX-compliant parser to unmarshal from XML.
Please see Example 1.1, “XML produced in introspection mode” for StAX-specific
unmarshal
methods added to
org.exolab.castor.xml.Unmarshaller
.
Added a section on how to access the properties as defined in the Castor properties file from within code.
Release 1.2.1:
: Added new
org.exolab.castor.xml.lenient.integer.validation
property to allow configuration of leniency for
validation for Java properties generated from
<xs:integer>
types during code generation.
Release 1.2:
: Access to the
org.exolab.castor.util.LocalConfiguration
class has been removed completely. To access the
properties as used by Castor from code, please refer
to the below section.
Release 1.1.3: Added special processing of proxied classes. The property org.exolab.castor.xml.proxyInterfaces allows you to specify a list of interfaces that such proxied objects implement. If your object implements one of these interfaces Castor will not use the class itself but its superclass at introspection or to find class mappings and ClassDescriptors.
Release 0.9.7:
Added new org.exolab.castor.persist.useProxies
property to allow configuration of JDBC proxy
classes. If enabled, JDBC proxy classes will be used
to wrap
java.sql.Connection
and
java.sql.PreparedStatement
instances, to allow for more detailed and complete
JDBC statements to be output during logging. When
turned off, no logging statements will be generated
at all.
Castor uses a configuration file for environmental
properties that are shared across all the Castor sub
systems. The configuration file is specified as a Java
properties file with the name castor.properties
.
By definition, a default configuration file is included with the Castor XML JAR. Custom properties can be supplied using one of the following methods. Please note that the custom properties specified will override the default configuration.
Place a file named castor.properties
anywhere on the classpath of your application.
Place a file named castor.properties
in the working directory of your application.
Use the system property
org.castor.user.properties.location
to
specify the location of your custom properties.
Please note that Castor XML - upon startup - will try the methods given above in exactly the sequence as stated above; if it managed to find a custom property file using any of the given methods, it will cancel its search.
When running the provided examples, Castor will use the configuration file located in the examples directory which specifies additional debugging information as well as pretty printing of all produced XML documents.
The following properties are currently supported in the configuration file:
Table 1.15.
Name | Description | Values | Default | Since |
---|---|---|---|---|
org.exolab.castor.xml.introspector.primitive.nodetype |
Property specifying the type of XML node to use
for primitive values, either
element
or
attribute
|
element
or
attribute
|
attribute
| - |
org.exolab.castor.parser | Property specifying the class name of the SAX XML parser to use. | - | - | - |
org.exolab.castor.parser.validation | Specifies whether to perform XML document validation by default. |
true
and
false
|
false
| - |
org.exolab.castor.parser.namespaces | Specifies whether to support XML namespaces by default. |
false
and
true
|
false
| - |
org.exolab.castor.xml.nspackages | Specifies a list of XML namespace to Java package mappings. | - | - | - |
org.exolab.castor.xml.naming |
Property specifying the 'type' of the XML naming
conventions to use. Values of this property must
be either
mixed
,
lower
, or the name of a class which extends
org.exolab.castor.xml.XMLNaming
.
|
mixed
,
lower
, or the name of a class which extends
org.exolab.castor.xml.XMLNaming
|
lower
| - |
org.castor.xml.java.naming |
Property specifying the 'type' of the Java
naming conventions to use. Values of this
property must be either
null
or the name of a class which extends
link org.castor.xml.JavaNaming .
|
|
null
| - |
org.exolab.castor.marshalling.validation | Specifies whether to use validation during marshalling. |
false
or
true
|
true
| - |
org.exolab.castor.indent | Specifies whether XML documents (as generated at marshalling) should use indentation or not. |
false
or
true
|
false
| - |
org.exolab.castor.sax.features | Specifies additional features for the XML parser. | A comma separated list of SAX (parser) features (that might or might not be supported by the specified SAX parser). | http://apache.org/xml/features/disallow-doctype-decl | - |
org.exolab.castor.sax.features-to-disable | Specifies features to be disbaled on the underlying SAX parser. | A comma separated list of SAX (parser) features to be disabled. |
http://xml.org/sax/features/external-general-entities ,
http://xml.org/sax/features/external-parameter-entities ,
http://apache.org/xml/features/nonvalidating/load-external-dtd
|
1.0.4 |
org.exolab.castor.regexp | Specifies the regular expression validator to use. | A class that implements org.exolab.castor.util.RegExpValidator . | - | - |
org.exolab.castor.xml.strictelements | Specifies whether to apply strictness to elements when unmarshalling. When enabled, the existence of elements in the XML document, which cannot be mapped to a class, causes a {@link SAXException} to be thrown. If set to false, these 'unknown' elements are ignored. |
false
or
true
|
false
| - |
org.exolab.castor.xml.loadPackageMappings |
Specifies whether the ClassDescriptorResolver
should (automatically) search for and consult
with package mapping files (
.castor.xml
) to retrieve class descriptor information
|
false
or
true
|
true
| 1.0.2 |
org.exolab.castor.xml.serializer.factory | Specifying what XML serializers factory to use. | A class name | org.exolab.castor.xml.XercesXMLSerializerFactory | 1.0 |
org.exolab.castor.xml.lenient.sequence.order | Specifies whether sequence order validation should be lenient. |
false
or
true
|
false
| 1.1 |
org.exolab.castor.xml.lenient.id.validation | Specifies whether id/href validation should be lenient. |
false
or
true
|
false
| 1.1 |
org.exolab.castor.xml.proxyInterfaces | Specifies whether or not to search for an proxy interface at marshalling. If property is not empty the objects to be marshalled will be searched if they implement one of the given interface names. If the interface is implemented, the superclass will be marshalled instead of the class itself. | A list of proxy interfaces | - | 1.1.3 |
org.exolab.castor.xml.lenient.integer.validation |
Specifies whether validation for Java properties
generated from <xs:integer> should be
lenient, i.e. allow for
int
s as well.
|
false
or
true
|
false
| 1.2.1 |
org.exolab.castor.xml.version | Specifies the XML document version number to be used during marshalling; defaults to 1.0. |
1.0
or
1.1
|
1.0
| 1.3.2 |
Note | |
---|---|
As of Castor 1.3.3, the default values for org.exolab.castor.sax.features
and org.exolab.castor.sax.features-to-disable have changed to include/disable
selected features.
|
As of Castor 1.1, it is possible to read and set the value of properties programmatically using the
getProperty(String)
and setProperty(String,String)
on the following classes:
org.exolab.castor.xml.Unmarshaller
org.exolab.castor.xml.Marshaller
org.exolab.castor.xml.XMLContext
Whilst using the setter methods on the first two classes will change the settings of the respective instances only,
using the setProperty()
method on the org.exolab.castor.xml.XMLContext
class will change the configuration globally, and affect all org.exolab.castor.xml.Unmarshaller
and org.exolab.castor.xml.Marshaller
instances created thereafter using the
org.exolab.castor.xml.XMLContext.createUnmarshaller()
and
org.exolab.castor.xml.XMLContext.createMarshaller()
methods.
When developing using Castor, we recommend that you use the various
setLogWriter
methods to get detailed information and error
messages.
Using a logger with org.exolab.castor.mapping.Mapping
will
provide detailed information about mapping decisions made by Castor and
will show the SQL statements being used.
Using a logger with org.exolab.castor.jdo.JDO
will provide
trace messages that show when Castor is loading, storing, creating and
deleting objects. All database operations will appear in the log; if an
object is retrieved from the cache or is not modified, there will be no
trace of load/store operations.
Using a logger with org.exolab.castor.xml.Unmarshaller
will
provide trace messages that show conflicts between the XML document and
loaded objects.
A simple trace logger can be obtained from
org.exolab.castor.util.Logger
. This logger uses the
standard output stream, but prefixes each line with a short message
that indicates who generated it. It can also print the time and date of
each message. Since logging is used for warning messages and simple
tracing, Castor does not require a sophisticated logging mechanism.
Interested in integratating Castor's logging with Log4J? Then see this question in the JDO FAQ.
By default the marshaler writes XML documents without indentation. When
developing using Castor or when debugging an application that uses
Castor, it might be desireable to use indentation to make the XML
documents human-readable. To turn indentation on, modify the Castor
properties file, or create a new properties file in the classpath
(named castor.properties
) with the following content:
org.exolab.castor.indent=true
Indentation inflates the size of the generated XML documents, and also consumes more CPU. It is recommended not to use indentation in a production environment.
It is possible to disable the validation in the marshaling framework
by modifying the Castor properties file or by creating a new
properties file in the classpath (named castor.properties
)
with the following content:
org.exolab.castor.marshalling.validation=false
Note | |
---|---|
This only works with Castor-XML. |
To save time when writing your mappings, try using the auto-complete attribute of class. When using auto-complete, Castor will introspect your class and automatically fill in any missing fields.
Example:
<class name="com.acme.Foo" auto-complete="true"/>
This is also compatible with generated descriptor files. You can use a mapping file to override some of the behavior of a compiled descriptor by using auto-complete.
Note | |
---|---|
Be careful to make sure you use the exact field name as specified in the generated descriptor file in order to modify the behavior of the field descriptor! Otherwise, you'll probably end up with two fields being marshaled! |
Castor requires that classes have a public, no-argument constructor in order to provide the ability to marshal & unmarshal objects of that type.
create-method is an optional attribute to the <field>
mapping
element that can be used to overcome this restriction in cases where you
have an existing object model that consists of, say, singleton classes
where public, no-argument constructors must not be present by definition.
Assume for example that a class "A
" that you want to be able
to unmarshal uses a singleton class as one of its properties. When
attempting to unmarshal class "A
", you should get an exception
because the singleton property has no public no-arg constructor.
Assuming that a reference to the singleton can be obtained via a static
getInstance() method, you can add a "create method" to class A
like this:
public MySingleton getSingletonProperty() { return MySingleton.getInstance(); }
and in the mapping file for class A
, you can define
the singleton property like this:
<field name="mySingletonProperty" type="com.u2d.MySingleton" create-method="getSingletonProperty"> <bind-xml name="my-singleton-property" node="element" /> </field>
This illustrates how the create-method attribute is quite a useful mechanism for dealing with exceptional situations where you might want to take advantage of marshaling even when some classes do not have no-argument public constructors.
Note | |
---|---|
As of this writing, the specified create-method must
exist as a method in the current class (i.e. the class being described
by the current |
Castor allows control on the object being marshaled or unmarshaled by a set of two listener interfaces: MarshalListener and UnmarshalListener.
The MarshalListener interface located in org.exolab.castor.xml
listens to
two different events that are intercepted by the following methods:
preMarshal: this method is called before an object gets marshaled.
postMarshal: this method is called once an object has been marshaled.
The UnmarshalListener located also in org.castor.xml
listens to
four different events that are intercepted by the following methods:
initialized: this method is called once an object has been instantiated.
attributesProcessed: this method is called when the attributes have just been read and processed.
fieldAdded: this method is called when an object is added to a parent.
unmarshalled: this method is called when an object has been fully unmarshaled
Note: The UnmarshalListener
had been part of org.exolab.castor.xml
but as an extention of this interface had been required a new interface in org.castor.xml
was introduced. Currently the org.exolab.castor.xml.UnmarshalListener
interface
can still be used but is deprecated.
Sometimes we need to deal with a data format that Castor doesn't
support out-of-the-box, such as an unsupported Date/Time
representation, or we want to wrap and unwrap fields in Wrapper
objects to get the desired XML output without changing our object
model. To handle these cases Castor allows specifying a custom
org.exolab.castor.mapping.FieldHandler
which can do these varying conversions during calls to the fields
setter and getter methods.
Note | |
---|---|
The FieldHandler is the basic interface used by the Castor Framework when accessing field values or setting them. By specifying a custom FieldHandler in the mapping file we can basically intercept the calls to retrieve or set a field's value and do whatever conversions are necessary. |
When a writing a FieldHandler handler we need to provide implementations of the various methods specified in the FieldHandler interface. The main two methods are the getValue and setValue methods which will basically handle all our conversion code. The other methods provide ways to create a new instance of the field's value or reset the field value.
Tip | |
---|---|
It's actually even easier to write custom field handlers if we use a GeneralizedFieldHandler. See more details in Section 1.7.3, “Writing a GeneralizedFieldHandler” |
Let's take a look at how to convert a date in the format YYYY-MM-DD using
a custom FieldHandler. We want to marshal the following XML input file text.xml
:
<?xml version="1.0"?> <root>2004-05-10</root>
The class we'll be marshalling from and unmarshalling to looks as follows:
import java.util.Date; public class Root { private Date _date; public Root() { super(); } public Date getDate() { return _date; } public void setDate(final Date date) { _date = date; }
So we need to write a custom FieldHandler that takes the input String and converts it into the proper java.util.Date instance:
import org.exolab.castor.mapping.FieldHandler; import org.exolab.castor.mapping.FieldDescriptor; import org.exolab.castor.mapping.ValidityException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; /** * The FieldHandler for the Date class * */ public class MyDateHandler implements FieldHandler { private static final String FORMAT = "yyyy-MM-dd"; /** * Creates a new MyDateHandler instance */ public MyDateHandler() { super(); } /** * Returns the value of the field from the object. * * @param object The object * @return The value of the field * @throws IllegalStateException The Java object has changed and * is no longer supported by this handler, or the handler is not * compatible with the Java object */ public Object getValue(final Object object) throws IllegalStateException { Root root = (Root)object; Date value = root.getDate(); if (value == null) return null; SimpleDateFormat formatter = new SimpleDateFormat(FORMAT); Date date = (Date)value; return formatter.format(date); } /** * Sets the value of the field on the object. * * @param object The object * @param value The new value * @throws IllegalStateException The Java object has changed and * is no longer supported by this handler, or the handler is not * compatible with the Java object * @throws IllegalArgumentException The value passed is not of * a supported type */ public void setValue(Object object, Object value) throws IllegalStateException, IllegalArgumentException { Root root = (Root)object; SimpleDateFormat formatter = new SimpleDateFormat(FORMAT); Date date = null; try { date = formatter.parse((String)value); } catch(ParseException px) { throw new IllegalArgumentException(px.getMessage()); } root.setDate(date); } /** * Creates a new instance of the object described by this field. * * @param parent The object for which the field is created * @return A new instance of the field's value * @throws IllegalStateException This field is a simple type and * cannot be instantiated */ public Object newInstance(Object parent) throws IllegalStateException { //-- Since it's marked as a string...just return null, //-- it's not needed. return null; } /** * Sets the value of the field to a default value. * * Reference fields are set to null, primitive fields are set to * their default value, collection fields are emptied of all * elements. * * @param object The object * @throws IllegalStateException The Java object has changed and * is no longer supported by this handler, or the handler is not * compatible with the Java object */ public void resetValue(Object object) throws IllegalStateException, IllegalArgumentException { ((Root)object).setDate(null); } }
Tip | |
---|---|
The newInstance method should return null for immutable types. |
Note | |
---|---|
There is also an
|
In order to tell Castor that we want to use our Custom FieldHandler
we must specify it in the mapping file mapping.xml
:
<?xml version="1.0"?> <mapping> <class name="Root"> <field name="date" type="string" handler="MyDateHandler"> <bind-xml node="text"/> </field> </class> </mapping>
We can now use a simple Test class to unmarshal our XML document:
import java.io.*; import org.exolab.castor.xml.*; import org.exolab.castor.mapping.*; public class Test { public static void main(String[] args) { try { //--load mapping Mapping mapping = new Mapping(); mapping.loadMapping("mapping.xml"); System.out.println("unmarshalling root instance:"); System.out.println(); Reader reader = new FileReader("test.xml"); Unmarshaller unmarshaller = new Unmarshaller(Root.class); unmarshaller.setMapping(mapping); Root root = (Root) unmarshaller.unmarshal(reader); reader.close(); System.out.println("Root#getDate : " + root.getDate()); } catch (Exception e) { e.printStackTrace(); } } }
Now simply compile the code and run!
% java Test unmarshalling root instance: Root#getDate : Mon May 10 00:00:00 CDT 2004
After running our test program we can see that Castor invoked our custom FieldHandler and we got our properly formatted date in our Root.class.
A org.exolab.castor.mapping.GeneralizedFieldHandler
is an extension of FieldHandler interface
where we simply write the conversion methods and Castor will automatically
handle the underlying get/set operations. This allows us to re-use the
same FieldHandler for fields from different classes that require the
same conversion.
Note | |
---|---|
Note: Currently the GeneralizedFieldHandler cannot be used from a binding-file for use with the SourceGenerator, an enhancement patch will be checked into SVN for this feature, shortly after 0.9.6 final is released. |
The same FieldHandler we used above can be written as a GeneralizedFieldHandler as such:
import org.exolab.castor.mapping.GeneralizedFieldHandler; import org.exolab.castor.mapping.FieldDescriptor; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; /** * The FieldHandler for the Date class * */ public class MyDateHandler extends GeneralizedFieldHandler { private static final String FORMAT = "yyyy-MM-dd"; /** * Creates a new MyDateHandler instance */ public MyDateHandler() { super(); } /** * This method is used to convert the value when the * getValue method is called. The getValue method will * obtain the actual field value from given 'parent' object. * This convert method is then invoked with the field's * value. The value returned from this method will be * the actual value returned by getValue method. * * @param value the object value to convert after * performing a get operation * @return the converted value. */ public Object convertUponGet(Object value) { if (value == null) return null; SimpleDateFormat formatter = new SimpleDateFormat(FORMAT); Date date = (Date)value; return formatter.format(date); } /** * This method is used to convert the value when the * setValue method is called. The setValue method will * call this method to obtain the converted value. * The converted value will then be used as the value to * set for the field. * * @param value the object value to convert before * performing a set operation * @return the converted value. */ public Object convertUponSet(Object value) { SimpleDateFormat formatter = new SimpleDateFormat(FORMAT); Date date = null; try { date = formatter.parse((String)value); } catch(ParseException px) { throw new IllegalArgumentException(px.getMessage()); } return date; } /** * Returns the class type for the field that this * GeneralizedFieldHandler converts to and from. This * should be the type that is used in the * object model. * * @return the class type of of the field */ public Class getFieldType() { return Date.class; } /** * Creates a new instance of the object described by * this field. * * @param parent The object for which the field is created * @return A new instance of the field's value * @throws IllegalStateException This field is a simple * type and cannot be instantiated */ public Object newInstance(Object parent) throws IllegalStateException { //-- Since it's marked as a string...just return null, //-- it's not needed. return null; } }
Everything else is the same. So we can re-run our test case using this GeneralizedFieldHandler and we'll get the same result. The main difference is that we implement the convertUponGet and convertUponSet methods.
Notice that we never reference the Root
class in our GeneralizedFieldHandler
. This allows us
to use the same exact FieldHandler
for any field
that requires this type of conversion.
In some situations, the GeneralizedFieldHandler
might not
provide sufficient flexibility. Suppose your XML document uses more than one
date format. You could solve this by creating a GeneralizedFieldHandler
subclass per date format, but that would lead to code duplication, which in
itself is not desirable.
A ConfigurableFieldHandler
is a FieldHandler
that can be configured in the mapping file
with any kind and any number of parameters. You can simply configure two (or more)
instances of the same ConfigurableFieldHandler
class
with different date format patterns.
Here's a mapping file that uses a ConfigurableFieldHandler
to marshal and unmarshal the
date field, similar to the preceding examples:
<?xml version="1.0"?> <mapping> <field-handler name="myHandler" class="FieldHandlerImpl"> <param name="date-format" value="yyyyMMddHHmmss"/> </field-handler> <class name="Root"> <field name="date" type="string" handler="myHandler"/> </class> </mapping>
The field-handler element defines the
ConfigurableFieldHandler
.
The class must be an implementation of the
org.exolab.castor.mapping.ConfigurableFieldHandler
interface. This
instance is configured with a date format. However, each implementation can decide
which, and how many parameters to use.
The field handler instance is referenced by the field element, using the handler attribute.
Here's the ConfigurableFieldHandler implementation:
import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.text.ParseException; import java.text.SimpleDateFormat; import java.text.DateFormat; import java.util.Date; import java.util.Properties; import org.exolab.castor.mapping.ConfigurableFieldHandler; import org.exolab.castor.mapping.FieldHandler; import org.exolab.castor.mapping.GeneralizedFieldHandler; import org.exolab.castor.mapping.ValidityException; public class FieldHandlerImpl implements FieldHandler, ConfigurableFieldHandler { private DateFormat formatter; public void setConfiguration(final Properties config) throws ValidityException { String pattern = config.getProperty("date-format"); if (pattern == null) { throw new ValidityException("Required parameter \"date-format\" is missing for FieldHandlerImpl."); } try { formatter = new SimpleDateFormat(pattern); } catch (IllegalArgumentException e) { throw new ValidityException("Pattern \""+pattern+"\" is not a valid date format."); } } /** * Returns the value of the field from the object. * * @param object The object * @return The value of the field * @throws IllegalStateException The Java object has changed and * is no longer supported by this handler, or the handler is not * compatible with the Java object */ public Object getValue(Object object) throws IllegalStateException { Root root = (Root)object; Date value = root.getDate(); if (value == null) return null; return formatter.format(value); } /** * Sets the value of the field on the object. * * @param object The object * @param value The new value * @throws IllegalStateException The Java object has changed and * is no longer supported by this handler, or the handler is not * compatible with the Java object * @throws IllegalArgumentException The value passed is not of * a supported type */ public void setValue(Object object, Object value) throws IllegalStateException, IllegalArgumentException { Root root = (Root)object; Date date = null; try { date = formatter.parse((String)value); } catch(ParseException px) { throw new IllegalArgumentException(px.getMessage()); } root.setDate(date); } /** * Creates a new instance of the object described by this field. * * @param parent The object for which the field is created * @return A new instance of the field's value * @throws IllegalStateException This field is a simple type and * cannot be instantiated */ public Object newInstance(Object parent) throws IllegalStateException { //-- Since it's marked as a string...just return null, //-- it's not needed. return null; } /** * Sets the value of the field to a default value. * * Reference fields are set to null, primitive fields are set to * their default value, collection fields are emptied of all * elements. * * @param object The object * @throws IllegalStateException The Java object has changed and * is no longer supported by this handler, or the handler is not * compatible with the Java object */ public void resetValue(Object object) throws IllegalStateException, IllegalArgumentException { ((Root)object).setDate(null); } }
This implementation is similar to the first MyDateHandler example on this page, except that is adds a setConfiguration method as specified by the ConfigurableFieldHandler interface. All parameters that are configured in the mapping file will be passed in as a Properties object. The implementing method is responsible for processing the configuration data.
As a convenience, org.exolab.castor.mapping.AbstractFieldHandler already implements ConfigurableFieldHandler. However, the setConfiguration method is not doing anything. Any subclass of AbstractFieldHandler only has to override this method to leverage the configuration capabilities. Since AbstractFieldHandler and its subclass GeneralizedFieldHandler are useful abstract classes, you'd probably want to use them anyway. It eliminates the need to implement the ConfigurableFieldHandler interface yourself.
Imagine a scenario where you want to use above ConfigurableFieldHandler
instance for more than one field - a valid use case as it promotes reuse.
<?xml version="1.0"?> <mapping> <field-handler name="myFirstHandler" class="FieldHandlerImpl"> <param name="date-format" value="yyyyMMddHHmmss"/> </field-handler> <field-handler name="mySecondHandler" class="FieldHandlerImpl"> <param name="date-format" value="yyyy-MM-ddHH:mm:ss"/> </field-handler> <class name="Root"> <field name="firstDate" type="string" handler="myFirstHandler"/> <field name="secondDate" type="string" handler="myFirstHandler"/> <field name="thirdDate" type="string" handler="mySecondHandler"/> </class> </mapping>
For this to work, there's one more thing you will have to do: your
ConfigurableFieldHandler
implementation has to implement the
ClonableFieldHandlerMarker
interface and implement
the copyFieldHandler()
method. As indicated by the name,
please return a clone/copy of your FieldHandler
instance ... and you
are all set.
A simplified sample implementation could look as follows, extending the
FieldHandlerImpl
class from the previous section:
public class FieldHandlerImpl implements FieldHandler, ConfigurableFieldHandler, ClonableFieldHandlerMarker { private DateFormat format; ... public void setFormat(DateFormat format) { this.format = format; } @Override public FieldHandler copyFieldHandler() { FieldHandlerImpl handler = new FieldHandlerImpl(); handler.setFormat(this.getFormat()); return handler; } }
A number of classes such as type-safe enum style classes have no constructor, but instead have some sort of static factory method used for converting a string value into an instance of the class. With a custom FieldHandler we can allow Castor to work nicely with these types of classes.
Tip | |
---|---|
Castor XML automatically supports these types of classes if they have a specific method: public static {Type} valueOf(String)
|
Note | |
---|---|
We're working on the same support for Castor JDO |
Even though Castor XML supports the "valueOf" method type-safe enum style classes, we'll show you how to write a custom handler for these classes anyway since it's useful for any type of class regardless of the name of the factory method.
Let's look at how to write a handler for the following type-safe enum style class, which was actually generated by Castor XML (javadoc removed for brevity):
import java.io.Serializable; import java.util.Enumeration; import java.util.Hashtable; public class Color implements java.io.Serializable { public static final int RED_TYPE = 0; public static final Color RED = new Color(RED_TYPE, "red"); public static final int GREEN_TYPE = 1; public static final Color GREEN = new Color(GREEN_TYPE, "green"); public static final int BLUE_TYPE = 2; public static final Color BLUE = new Color(BLUE_TYPE, "blue"); private static java.util.Hashtable _memberTable = init(); private int type = -1; private java.lang.String stringValue = null; private Color(int type, java.lang.String value) { super(); this.type = type; this.stringValue = value; } //-- test.types.Color(int, java.lang.String) public static java.util.Enumeration enumerate() { return _memberTable.elements(); } //-- java.util.Enumeration enumerate() public int getType() { return this.type; } //-- int getType() private static java.util.Hashtable init() { Hashtable members = new Hashtable(); members.put("red", RED); members.put("green", GREEN); members.put("blue", BLUE); return members; } //-- java.util.Hashtable init() public java.lang.String toString() { return this.stringValue; } //-- java.lang.String toString() public static Color valueOf(java.lang.String string) { Object obj = null; if (string != null) obj = _memberTable.get(string); if (obj == null) { String err = "'" + string + "' is not a valid Color"; throw new IllegalArgumentException(err); } return (Color) obj; } //-- test.types.Color valueOf(java.lang.String) }
The GeneralizedFieldHandler for the above Color class is as follows (javadoc removed for brevity):
import org.exolab.castor.mapping.GeneralizedFieldHandler; import org.exolab.castor.mapping.FieldDescriptor; /** * The FieldHandler for the Color class **/ public class ColorHandler extends GeneralizedFieldHandler { public ColorHandler() { super(); } public Object convertUponGet(Object value) { if (value == null) return null; Color color = (Color)value; return color.toString(); } public Object convertUponSet(Object value) { return Color.valueOf((String)value); } public Class getFieldType() { return Color.class; } public Object newInstance( Object parent ) throws IllegalStateException { //-- Since it's marked as a string...just return null, //-- it's not needed. return null; } }
That's all there really is to it. Now we just need to hook this up to our mapping file and run a sample test.
If we have a root class Foo as such:
public class Foo { private Color _color = null; private int _size = 0; private String _name = null; public Foo() { super(); } public Color getColor() { return _color; } public String getName() { return _name; } public int getSize() { return _size; } public void setColor(Color color) { _color = color; } public void setName(String name) { _name = name; } public void setSize(int size) { _size = size; } }
Our mapping file would be the following:
<?xml version="1.0"?> <mapping> <class name="Foo"> <field name="size" type="integer"> <bind-xml node="element"/> </field> <field name="name" type="string"/> <field name="color" type="string" handler="ColorHandler"/> </class> </mapping>
We can now use our custom FieldHandler to unmarshal the following xml input:
<?xml version="1.0"?> <foo> <name>MyFoo</name> <size>345</size> <color>blue</color> </foo>
A sample test class is as follows:
import java.io.*; import org.exolab.castor.xml.*; import org.exolab.castor.mapping.*; public class Test { public static void main(String[] args) { try { //--load mapping Mapping mapping = new Mapping(); mapping.loadMapping("mapping.xml"); System.out.println("unmarshalling Foo:"); System.out.println(); Reader reader = new FileReader("test.xml"); Unmarshaller unmarshaller = new Unmarshaller(Foo.class); unmarshaller.setMapping(mapping); Foo foo = (Foo) unmarshaller.unmarshal(reader); reader.close(); System.out.println("Foo#size : " + foo.getSize()); System.out.print("Foo#color: "); if (foo.getColor() == null) { System.out.println("null"); } else { System.out.println(foo.getColor().toString()); } PrintWriter pw = new PrintWriter(System.out); Marshaller marshaller = new Marshaller(pw); marshaller.setMapping(mapping); marshaller.marshal(foo); pw.flush(); } catch (Exception e) { e.printStackTrace(); } } }
Note | |
---|---|
With Castor 0.9.6 and later, the GeneralizedFieldHandler automatically supports iterating over the items of a collection and passing them one-by-one to the convertUponGet.
For backward compatibility or to handle the collection iteration yourself,
simply add the following to the constructor of your
setCollectionIteration(false);
|
If you're going to be using custom field handlers for collection fields with
a GeneralizedFieldHandler
using versions of Castor
prior to 0.9.6, then you'll need to handle the collection iteration yourself in the
convertUponGet method.
If you're not using a GeneralizedFieldHandler
, then
you'll need to handle the collection iteration yourself in the
FieldHandler#getValue() method.
Tip | |
---|---|
Since Castor incrementally adds items to collection fields, there
usually is no need to handle collections directly in the
convertUponSet method (or the setValue() for those
not using |
There are many users of Castor XML who (want to) use Castor XML in in high-volume applications. To fine-tune Castor for such an environment, it is necessary to understand many of the product features in detail and to be able to balance their use according to the application needs. Even though many of these features are documented in various places, people frequently asked for a 'best practices' document, a document that brings together these technical topics in one place and that presents them as a set of easy-to-use recipes.
Please be aware that this document is under construction. But still we believe that this document -- even when in its conception phase -- provides valuable information to users of Castor XML.
It is not generally recommended to generate code into the default package, especially since code in the default package cannot be referenced from code in any other package.
Additionally, we recommend that generated code go into a different
package then the code that makes use of the generated code. For
example, if your application uses Castor to process an XML
configuration file that is used by code in the package
org.example.userdialog
then we do not recommend that the
generated code also go into that package. However, it would be
reasonable to generate source to process this XML configuration file
into the package org.example.userdialog.xmlconfig
.
Creating instances of org.exolab.castor.xml.Marshaller
and
org.exolab.castor.xml.Unmarshaller
for the purpose of XML
data binding is easy to achieve at the API usage level. However,
details of API use have an impact on application performance; each
instance creation involves setup operations.
This is generally not an issue for one-off invocations; however, in a multi-threaded, high volume use scenario this can be become a serious issue. Internally, Castor uses a collection of Descriptor classes to keep information about the Java entities to be marshaled and unmarshaled. With each instance creation of (Un)Marshaller, this collection will be built from scratch (again and again).
To avoid this initial configuration 'penalty', Castor allows you to
cache these Descriptor classes through its
org.exolab.castor.xml.ClassDescriptorResolver
component.
This cache allows reuse of these Descriptor instances between
(Un)Marshaller invocations.
With the introduction of the new org.exolab.castor.xml.XMLContext
class, the use of a
ClassDescriptorResolver
has been greatly simplified in that such
an instance is managed by the XMLContext per default. As such, there's no need
to pass a ClassDescriptorResolver
instance to
Marshaller
/
Unmarshaller
instances anymore, as this is done automatically
when such instances are created through
org.exolab.castor.xml.XMLContext.createMarshaller()
org.exolab.castor.xml.XMLContext.createUnmarshaller()
For example, to create a Marshaller
instance that is pre-configured
with an instance of ClassDescriptorResolver
, use the following code
fragment:
Mapping mapping = new Mapping(); mapping.loadMapping(new InputSource(...)); XMLContext context = new XMLContext(); context.addMapping(mapping); Marshaller marshaller = context.createMarshaller();
In the case where no mapping file is used, it is still possible
to instruct the org.exolab.castor.xml.XMLContext
to
pre-load class descriptors for a given
package via the methods enlisted below.
As above, create an instance of org.exolab.castor.xml.XMLContext
and configure it according to your needs as shown below:
XMLContext context = new XMLContext(); context.addPackage("your.package.name"); Marshaller marshaller = context.createMarshaller();
The org.exolab.castor.xml.XMLContext
class
provides for various methods to load class descriptors for individual classes
and/or packages.
Table 1.16. Methods on XMLContext to create Un-/Marshaller objects
Method | Description | .castor.cdr |
---|---|---|
addClass(Class) on org.exolab.castor.xml.XMLContext | Loads the class descriptor for one class. | n/a |
addClass(Class[]) on org.exolab.castor.xml.XMLContext | Loads the class descriptors for a collection of classes. | n/a |
addPackage(String) on org.exolab.castor.xml.XMLContext | Loads the class descriptor for all classes in the defined package. | Required |
addPackages(String[]) on org.exolab.castor.xml.XMLContext | Loads the class descriptor for all classes in the defined packages. | Required |
Note | |
---|---|
For some of the methods, pre-loading class descriptords will only work if you provide
the |
When you do not use the XMLContext
class, you will have
to manually manage your
org.exolab.castor.xml.XMLClassDescriptorResolver
. To do
so, first create an instance of
org.exolab.castor.xml.XMLClassDescriptorResolver
using the following code fragment:
XMLClassDescriptorResolver classDescriptorResolver =
(XMLClassDescriptorResolver) ClassDescriptorResolverFactory.createClassDescriptorResolver(BindingType.XML);
MappingUnmarshaller mappingUnmarshaller = new MappingUnmarshaller();
MappingLoader mappingLoader =
mappingUnmarshaller.getMappingLoader(mapping, BindingType.XML);
classDescriptorResolver.setMappingLoader(mappingLoader);
and then reuse this instance as shown below:
Unmarshaller unmarshaller = new Unmarshaller();
unmarshaller.setResolver(classDescriptorResolver);
unmarshaller.unmarshal(...);
When you are not using a mapping file, but you have generated Java
classes and their corresponding descriptor classes using the Castor
XML code generator, you might want to instruct the
org.exolab.castor.xml.XMLClassDescriptorResolver
to
pre-load class descriptors (as enumerated explicitly
or for a given package) using various add*
methods.
As above, create an instance of
org.exolab.castor.xml.XMLClassDescriptorResolver">XMLClassDescriptorResolver
using the following code fragment:
XMLClassDescriptorResolver classDescriptorResolver = (XMlClassDescriptorResolver) ClassDescriptorResolverFactory.createClassDescriptorResolver(BindingType.XML); classDescriptorResolver.setClassLoader(...); classDescriptorResolver.addClass("your.package.name.A"); classDescriptorResolver.addClass("your.package.name.B"); classDescriptorResolver.addClass("your.package.name.C");
and then reuse this instance as shown above. Alternatively, add complete packages to the resolver configuration as follows:
XMLClassDescriptorResolver classDescriptorResolver = (XMlClassDescriptorResolver)
ClassDescriptorResolverFactory.createClassDescriptorResolver(BindingType.XML);
classDescriptorResolver.setClassLoader(...);
classDescriptorResolver.addPackage("your.package.name");
The org.exolab.castor.xml.XMLClassDescriptorResolver
interface provides various other methods to load class descriptors for individual
classes and/or packages.
Table 1.17. blah
Method | Description | Requires .castor.cdr |
---|---|---|
addClass(String) | Loads the class descriptor for one class. | No |
addClass(String[]) | Loads the class descriptors for a collection of classes. | No |
addPackage(String) | Loads the class descriptors for all classes in the package defined. | Yes |
addPackages(String[]) | Loads the class descriptors for all classes in the package defined. | Yes |
Note | |
---|---|
For some of the methods, pre-loading class descriptords will only work if you provide
the |
This is a collection of HOW-TOs. The Castor project is actively seeking additional HOW-TO contributors to expand this collection. For information on how to do that, please see 'How to write a How-to'.
How to Author a How-To (Author wanted!)
How to Author an FAQ (Author wanted!)
How to Author a Code Snippet (Author wanted!)
How to Author Core Documentation (Author wanted!)
How to Contribute a Patch via Jira (Author wanted!)
This section provides answers to frequently answered questions, i.e. questions that have been asked repeatedly on one of the mailing lists. Please check with these F.A.Q.s frequently, as addressing questions that have been answered in the past already again and again places an unnecessary burden on the committers/contributors.
This section is structured along the lines of the following areas ...
Create a new instance of the
Marshaller
class and use the
setEncoding
method. You'll also need to make sure the encoding for
the Writer is set properly as well:
... String encoding = "ISO-8859-1"; FileOutputStream fos = new FileOutputStream("result.xml"); OutputStreamWriter osw = new OuputStreamWriter(fos, encoding); Marshaller marshaller = new Marshaller(osw); marshaller.setEncoding(encoding); ...
Note | |
---|---|
For Castor 0.9.5.2 only |
The issue occurs with newer versions of Xerces than the version 1.4 that ships with Castor. The older version works OK. For some reason, when the newer version of Xerces encounters an "xml" prefixed attribute, such as "xml:lang", it tries to automatically start a prefix mapping for "xml". Which, in my opinion, is technically incorrect. They shouldn't be doing that. According to the w3c, the "xml" prefix should never be declared.
The reason it started appearing in the new Castor (0.9.5.2), is because of a switch to SAX 2 by default during unmarshaling.
Solution: A built in work-around has been checked into the Castor SVN and will automatically exist in any post 0.9.5.2 releases. For those who are using 0.9.5.2 and can't upgrade, I found a simple workaround (tested with Xerces 2.5). At first I thought about disabling namespace processing in Xerces, but then realized that it's already disabled by default by Castor ... so I have no idea why they call #startPrefixMapping when namespace processing has been disabled. But in any event... explicitly enabling namespace processing seems to fix the problem:
in the
castor.properties
file, change the following line:
org.exolab.castor.parser.namespaces=false
to:
org.exolab.castor.parser.namespaces=true
Note | |
---|---|
This work-around has only been tested with Xerces 2.5 and above. |
The get method will be called a second time during the validation process. To prevent this from happening, simply disable validation on the Marshaller or Unmarshaller.
Cache the descriptors!
import org.exolab.castor.xml.ClassDescriptorResolver; import org.exolab.castor.xml.Unmarshaller; import org.exolab.castor.xml.util.ClassDescriptorResolverImpl; ... ClassDescriptorResolver cdr = new ClassDescriptorResovlerImpl(); ... Unmarshaller unm = new Unmarshaller(...); unm.setResolver(cdr);
By reusing the same ClassDescriptorResolver
any
time you create an Unmarshaller instance, you
will be reusing the existing class descriptors
previously loaded.
Disable validation
unm.setValidation(false);
Reuse objects
To cut down on object creation, you can reuse an existing object model, but be careful because this is an experimental feature. Create an Unmarshaller with your existing root object and set object reuse to true...
Unmarshaller unm = new
Unmarshaller(myObjectRoot);
unm.setReuseObjects(true);
If you have enabled pretty-printing (indenting), then disable it. The Xerces Serializer is much slower with indenting enabled.
Try changing parsers to something other than Xerces.
There are probably other approaches you can use as well, but those seem to be the most popular ones. Let us know if you have a solution that you think we should add here.
Use the
Unmarshaller#setIgnoreExtraElements()
method:
Unmarshaller unm = new Unmarshaller(...);
unm.setIgnoreExtraElements(true);
If any elements appear in the XML instance that Castor cannot find mappings for, they will be skipped.
You can also set the
org.exolab.castor.xml.strictelements
property in the
castor.properties
file:
org.exolab.castor.xml.strictelements=true
Castor loads the castor.properties in the following order:
From classpath (usually from the jar file)
From {java.home}/lib (if present)
From the local working directory
Each properties file overrides the previous. So you don't have to come up with a properties file with all the properties and values, just the ones you want to change. This also means you don't have to touch the properties file found in the jar file.
Note | |
---|---|
Note: You can also use
|
Make sure you are not using one of the static methods on the Marshaller/Unmarshaller. Any configuration changes that you make to the Marshaller or Unmarshaller are not available from the static methods.
Yes! We provide one such tool, see
org.exolab.castor.tools.MappingTool
. There are some
3rd party
tools as well.
For a specific field you can use a QName for the value of the bind-xml name attribute as such:
<bind-xml name="foo:bar" xmlns:foo="http://www.acme.com/foo"/>
Note: The namespace prefix is only used for qualification during the loading of the mapping, it is not used during Marshaling. To map namespace prefixes during marshaling you currently need to set these via the Marshaler directly.
For a class mapping, use the <map-to> element. For more information see the XML Mapping documentation .
For all versions of Castor:
To enable pretty-printing (indenting, line-breaks) just modify the castor.properties file and uncomment the following:
# True if all documents should be indented on output by default # #org.exolab.castor.indent=true
Note: This will slow down the marshalling process
If you are using Castor's default introspection to automatically map the objects into XML, then there is no guarantee on the order. It simply depends on the order in which the fields are returned to Castor using the Java reflection API.
Note: If you use a mapping file Castor will generate the XML in the order in which the mapping file is specified.
Not directly, however you can convert your DTD to an XML
Schema fairly easily. We provide a tool (
org.exolab.castor.xml.dtd.Converter
) to do this. You can also use any number of 3rd-party
tools such as XML Spy or XML Authority.
Also: I used the source code generator, but all my xml element names are getting marshaled as lowercase with hyphens, what's up with that?
Solution: Are the generated class descriptors compiled? Make sure they get compiled along with the source code for the object model.
Example: Castor generates the following:
import types.Foo;
instead of:
import com.acme.types.Foo;
This usually happens when the namespaces for the imported schemas have not been mapped to appropriate java packages in the castorbuilder.properties file.
Solution:
Make sure the
castorbuilder.properties
is in your classpath when you run the
SourceGenerator.
Uncomment and edit the
org.exolab.castor.builder.nspackages
property. Make sure to copy the value of the
imported namespace exactly as it's referred
to in the schema (i.e. trailing slashes and
case-sensitivity matter!).
For those using 0.9.5.1, you'll need to upgrade due to a bug that is fixed in later releases.
For Castor 0.9.4 and above:
Castor JDO requires a reference to the actual collection
to be returned from the get-method. By default the
source generator does not provide such a method. To
enable such methods to be created, simple add the
following line to your
castorbuilder.properties
file:
org.exolab.castor.builder.extraCollectionMethods=true
Note: The default
castorbuilder.properties
file has this line commented out. Simply uncomment it.
Your mapping file will also need to be updated to include the proper set/get method names.
Yes! We provide such a tool. Please see
org.exolab.castor.xml.schema.util.XMLInstance2Schema
. It's not 100% perfect, but it does a reasonable job.
To enable XML validation at the parser level, please add
properties to your
castor.properties
file as follows:
org.exolab.castor.parser.namespaces=true org.exolab.castor.sax.features=http://xml.org/sax/features/validation,\ http://apache.org/xml/features/validation/schema,\ http://apache.org/xml/features/validation/schema-full-checking
Please note that the example given relies on the use of
Apache Xerces, hence the
apache.org
properties; similar options should exist for other
parsers.
When using a custom FieldHandlerFactory as in the following example
Mapping mapping = ... ; FieldHandlerFactoyt factory = ...; Marshaller m = new Marshaller(writer); ClassDescriptorResolverImpl cdr = new ClassDescriptorResolverImpl(); cdr.getIntrospector().addFieldHandlerFactory(factory); m.setResolver(cdr); marshaller.setMapping(mapping);
please make sure that you set the mapping file
after
you set the ClassDescriptorResolver. You will note the
following in the Javadoc for
org.exolab.castor.xml.Marshaller.html#setResolver(org.exolab.castor.xml.ClassDescriptorResolver)
:
Note | |
---|---|
Note: This method will nullify any Mapping currently being used by this Marshaller |
Yes and no. It actually depends. When requiring pretty printing during marshalling, Castor internally relies on Apache's Xerces to implement this feature. As such, when not using this feature, Xerces is not a requirement, and any JAXP-compliant XML parser can be used (for unmarshalling).
In other words, with the latter use case, you do not have to download (and use) Xerces separetely.
No. Starting with release 1.1, we have added support for using the Xerces instance as shipped with the JRE/JDK for serialization. As such, for Java 5.0 users, this removes the requirement to download Xerces separately when wanting to use 'pretty printing' with Castor XML during marshalling.
To enable this feature, please change the following
properties in your
local
castor.properties
file (thus redefining the default value) as shown below:
# Defines the XML parser to be used by Castor. # The parser must implement org.xml.sax.Parser. org.exolab.castor.parser=org.xml.sax.helpers.XMLReaderAdapter # Defines the (default) XML serializer factory to use by Castor, which must # implement org.exolab.castor.xml.SerializerFactory; default is # org.exolab.castor.xml.XercesXMLSerializerFactory org.exolab.castor.xml.serializer.factory=org.exolab.castor.xml.XercesJDK5XMLSerializerFactory # Defines the default XML parser to be used by Castor. org.exolab.castor.parser=com.sun.org.apache.xerces.internal.parsers.SAXParser